package com.quanxiaoha.xiaohashu.user.biz.runner;


import cn.hutool.core.collection.CollUtil;
import com.google.common.collect.Maps;
import com.quanxiaoha.framework.common.util.JsonUtils;
import com.quanxiaoha.framework.redis.constant.RedisKeyConstants;
import com.quanxiaoha.xiaohashu.user.biz.domain.dataobject.PermissionDO;
import com.quanxiaoha.xiaohashu.user.biz.domain.dataobject.RoleDO;
import com.quanxiaoha.xiaohashu.user.biz.domain.dataobject.RolePermissionDO;
import com.quanxiaoha.xiaohashu.user.biz.service.PermissionService;
import com.quanxiaoha.xiaohashu.user.biz.service.RolePermissionService;
import com.quanxiaoha.xiaohashu.user.biz.service.RoleService;
import jakarta.annotation.Resource;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @Author: jingyan_liu
 * @CreateTime: 2025-07-11
 * @Description:
 * @Version: 1.0
 */


@Component
@Slf4j
@RequiredArgsConstructor
public class PushRolePermissions2RedisRunner implements ApplicationRunner {
	
	// 权限同步标记 Key
	private static final String PUSH_PERMISSION_FLAG = "push.permission.flag";
	private final RoleService roleService;
	private final RolePermissionService rolePermissionService;
	private final PermissionService permissionService;
	@Resource
	private RedisTemplate<String, String> redisTemplate;
	
	@Override
	public void run(ApplicationArguments args) {
		log.info("==> 服务启动，开始同步角色权限数据到 Redis 中...");
		
		try {
			// 是否能够同步数据: 原子操作，只有在键 PUSH_PERMISSION_FLAG 不存在时，才会设置该键的值为 "1"，并设置过期时间为 1 天
			boolean canPushed = Boolean.TRUE.equals(redisTemplate.opsForValue().setIfAbsent(PUSH_PERMISSION_FLAG, "1", 1, TimeUnit.DAYS));
			
			// 如果无法同步权限数据
			if (!canPushed) {
				log.warn("==> 角色权限数据已经同步至 Redis 中，不再同步...");
				return;
			}
			
			//查询所有被启用的角色(id,roleKey,roleName)
			List<RoleDO> roleDOS = roleService.lambdaQuery()
					.eq(RoleDO::getStatus, 0)
					.select(RoleDO::getId, RoleDO::getRoleKey, RoleDO::getRoleName)
					.list();
			if (CollUtil.isNotEmpty(roleDOS)) {
				List<Long> roleIds = roleDOS.stream().map(RoleDO::getId).toList();
				//根据所有的roleId查询所对应的permissionId
				List<RolePermissionDO> rolePermissionDOS = rolePermissionService.lambdaQuery().in(RolePermissionDO::getRoleId, roleIds)
						.select(RolePermissionDO::getPermissionId, RolePermissionDO::getRoleId)
						.list();
				Map<Long, List<Long>> roleIdPermissionIdsMap = rolePermissionDOS.stream().collect(
						Collectors.groupingBy(RolePermissionDO::getRoleId,
								Collectors.mapping(RolePermissionDO::getPermissionId, Collectors.toList()))
				);
				//查询 APP 端所有被启用的权限
				List<PermissionDO> permissionDOS = permissionService.lambdaQuery()
						.eq(PermissionDO::getStatus, 0)
						.eq(PermissionDO::getType, 3)
						.select(PermissionDO::getId, PermissionDO::getName, PermissionDO::getPermissionKey)
						.list();
				// permissionId - permissionKey
				Map<Long, String> permissionIdpermissionKeyMap = permissionDOS.stream().collect(
						Collectors.toMap(PermissionDO::getId, PermissionDO::getPermissionKey)
				);
				// 组织 roleKey-permissionKey 关系
				Map<String, List<String>> roleKeyPermissionKeyMap = Maps.newHashMap();
				
				// 循环所有角色
				roleDOS.forEach(roleDO -> {
					// 当前角色 ID
					Long roleId = roleDO.getId();
					String roleKey = roleDO.getRoleKey();
					// 当前角色 ID 对应的permissionKey 集合
					List<Long> permissionIds = roleIdPermissionIdsMap.get(roleId);
					if (CollUtil.isNotEmpty(permissionIds)) {
						List<String> permissionKeys = new ArrayList<>();
						permissionIds.forEach(permissionId -> {
							// 根据权限 ID 获取具体的权限 DO 对象
							String permissionKey = permissionIdpermissionKeyMap.get(permissionId);
							if (Objects.nonNull(permissionKey)) {
								permissionKeys.add(permissionKey);
							}
						});
						roleKeyPermissionKeyMap.put(roleKey, permissionKeys);
					}
				});
				// 同步至 Redis 中，方便后续网关查询鉴权使用
				roleKeyPermissionKeyMap.forEach((roleKey, permissionKeys) -> {
					String key = RedisKeyConstants.buildRolePermissionsKey(roleKey);
					redisTemplate.opsForValue().set(key, JsonUtils.toJsonString(permissionKeys));
				});
				log.info("==> 服务启动，成功同步角色权限数据到 Redis 中...");
			}
		} catch (Exception e) {
			log.error("==> 同步角色权限数据到 Redis 中失败: ", e);
		}
	}
}
